package com.biboheart.adapter.adapter.impl;

import com.biboheart.adapter.support.enums.StructureTypeEnum;
import com.biboheart.adapter.support.exception.ConvertException;
import com.biboheart.adapter.support.utils.ObjectTypeUtils;

import java.util.*;

final class Target<T> {
    private final Manager<T> manager = new Manager<>();

    /**
     * 设置值
     * @param path 路径
     */
    public void value(Object value, String path, StructureTypeEnum type) {
        manager.putObject(value, path, type);
    }

    // 取结果
    public T result(Class<T> clazz) {
        return manager.createValue(clazz);
    }

    private static class Manager<T> {
        private StructureTypeEnum type;
        private final Map<String, Object> map = new HashMap<>();
        private final List<Object> list = new ArrayList<>();
        private Object obj = null;

        @SuppressWarnings("unchecked")
        private void putObject(Object value, String path, StructureTypeEnum structureType) throws ConvertException {
            if ("".equals(path)) {
                type = structureType;
                if (structureType.equals(StructureTypeEnum.STRUCTURE_TYPE_LIST)) {
                    if (null != value) {
                        list.addAll(ObjectTypeUtils.convertType(value, List.class));
                    }
                } else if (structureType.equals(StructureTypeEnum.STRUCTURE_TYPE_PRIMITIVE)) {
                    obj = value;
                }
                return;
            }
            if (StructureTypeEnum.STRUCTURE_TYPE_PRIMITIVE.equals(type)) {
                return;
            }
            Object parent = getParent(null, path, null);
            if (null == parent) {
                throw new ConvertException("转换出错: 写值路径[" + path + "]的父节点为空");
            }
            if (ObjectTypeUtils.isPrimitive(parent)) {
                return;
            }
            String[] split = path.split("\\.");
            String name = split[split.length - 1];
            if (StructureTypeEnum.STRUCTURE_TYPE_LIST.equals(structureType)) {
                if (null == value) {
                    value = new ArrayList<>();
                }
            } else if (StructureTypeEnum.STRUCTURE_TYPE_MAP.equals(structureType)) {
                if (null == value) {
                    value = new HashMap<>();
                }
            }
            if (parent instanceof List) {
                int index = Integer.parseInt(name);
                List<Object> list = ObjectTypeUtils.convertType(parent, List.class);
                if (index >= list.size()) {
                    list.addAll(new ArrayList<>(index - list.size() + 1));
                }
                list.set(index, value);
            } else {
                Map<String, Object> map = ObjectTypeUtils.convertType(parent, Map.class);
                map.put(name, value);
            }
        }

        private Object getParent(Object obj, String path, List<String> pathList) throws ConvertException {
            if (null == path || path.isEmpty()) {
                return null;
            }
            if (null == pathList) {
                String[] split = path.split("\\.");
                if (split.length <= 1) {
                    pathList = new ArrayList<>();
                } else {
                    pathList = new ArrayList<>(Arrays.asList(split).subList(0, split.length - 1));
                }
            }
            if (pathList.isEmpty()) {
                return obj;
            }
            String current = pathList.remove(0);
            if ("".equals(current)) {
                obj = StructureTypeEnum.STRUCTURE_TYPE_LIST.equals(type) ? list : map;
                return getParent(obj, path, pathList);
            }
            if (obj instanceof List && !ObjectTypeUtils.isNumeric(current)) {
                throw new ConvertException("转换出错: 写值路径[" + path + "]不匹配");
            }
            if (obj instanceof List) {
                int i = Integer.parseInt(current);
                List<?> list = ObjectTypeUtils.convertType(obj, List.class);
                if (null == list || i >= list.size()) {
                    return null;
                }
                obj = list.get(i);
            } else if (obj instanceof Map) {
                Map<?, ?> map = ObjectTypeUtils.convertType(obj, Map.class);
                obj = map.get(current);
            } else {
                return obj;
            }
            return getParent(obj, path, pathList);
        }

        private T createValue(Class<T> clazz) {
            if (StructureTypeEnum.STRUCTURE_TYPE_LIST.equals(type)) {
                if (list.isEmpty()) {
                    return null;
                }
                if (ObjectTypeUtils.isPrimitive(clazz)) {
                    return fromList(list, clazz);
                }
                return clazz.equals(List.class) ? clazz.cast(list) : ObjectTypeUtils.convertType(list.get(0), clazz);
            } else if (StructureTypeEnum.STRUCTURE_TYPE_MAP.equals(type)) {
                if (map.isEmpty()) {
                    return null;
                }
                return clazz.equals(List.class) ? clazz.cast(Collections.singletonList(map)) : ObjectTypeUtils.convertType(map, clazz);
            } else {
                return clazz.equals(List.class) ? clazz.cast(Collections.singletonList(obj)) : ObjectTypeUtils.convertType(obj, clazz);
            }
        }

        private T fromList(List<Object> objectList, Class<T> clazz) {
            if (clazz.equals(Integer.class)) {
                int res = 0;
                for (Object o : objectList) {
                    Integer io = ObjectTypeUtils.convertType(o, Integer.class);
                    if (null == io) {
                        continue;
                    }
                    res += io;
                }
                return clazz.cast(res);
            }
            if (clazz.equals(Long.class)) {
                long res = 0;
                for (Object o : objectList) {
                    Long io = ObjectTypeUtils.convertType(o, Long.class);
                    if (null == io) {
                        continue;
                    }
                    res += io;
                }
                return clazz.cast(res);
            }
            if (clazz.equals(Double.class)) {
                double res = 0;
                for (Object o : objectList) {
                    Double io = ObjectTypeUtils.convertType(o, Double.class);
                    if (null == io) {
                        continue;
                    }
                    res += io;
                }
                return clazz.cast(res);
            }
            if (clazz.equals(Float.class)) {
                float res = 0;
                for (Object o : objectList) {
                    Float io = ObjectTypeUtils.convertType(o, Float.class);
                    if (null == io) {
                        continue;
                    }
                    res += io;
                }
                return clazz.cast(res);
            }
            if (clazz.equals(String.class)) {
                StringBuilder sb = new StringBuilder();
                for (Object o : objectList) {
                    if (null == o) {
                        continue;
                    }
                    if (sb.length() > 0) {
                        sb.append(",");
                    }
                    sb.append(o);
                }
                return clazz.cast(sb.toString());
            }
            return ObjectTypeUtils.convertType(list.get(0), clazz);
        }
    }
}
